package com.mapforge.util;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;

/**
 * reads bits-at-a-time where the number of bits is between 1 and 32
 * 
 * @author Owen Astrachan
 * @version 1.0, July 2000
 */

public class BitInputStream {
	private InputStream myInput;
	private int myBitCount;
	private int myBuffer;
	private String myFilename;

	private static final int bmask[] = { 0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f,
			0x3f, 0x7f, 0xff, 0x1ff, 0x3ff, 0x7ff, 0xfff, 0x1fff, 0x3fff,
			0x7fff, 0xffff, 0x1ffff, 0x3ffff, 0x7ffff, 0xfffff, 0x1fffff,
			0x3fffff, 0x7fffff, 0xffffff, 0x1ffffff, 0x3ffffff, 0x7ffffff,
			0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff };

	private static final int BITS_PER_BYTE = 8;

	/**
	 * Construct a bit-at-a-time input stream from a file
	 */
	public BitInputStream(String filename) {
		myFilename = filename;
		rewind();
	}

	/**
	 * reset stream to beginning. The implementation creates a new stream.
	 */

	public void rewind() {
		try {
			close();
			myInput = new BufferedInputStream(new FileInputStream(myFilename));
		} catch (FileNotFoundException fnf) {
			System.err.println("error opening " + myFilename + " " + fnf);
		}
		reset();
	}

	/**
	 * Construct a bit-at-a-time input stream from <code>file</code>
	 * 
	 * @param file
	 *            is the File that is the source of the input
	 */
	public BitInputStream(File file) {
		try {
			myFilename = file.getCanonicalPath();
		} catch (IOException ioe) {
			System.err.println("error in opening " + file + " " + ioe);
		}
		rewind();
	}

	/**
	 * closes the input stream
	 */

	public void close() {
		try {
			if (myInput != null) {
				myInput.close();
			}
		} catch (java.io.IOException ioe) {
			System.err.println("error closing bit stream " + ioe);
		}
	}

	/**
	 * returns the number of bits requested as rightmost bits in returned value,
	 * returns -1 if not enough bits available to satisfy the request
	 * 
	 * @param howManyBits
	 *            is the number of bits to read and return
	 * @return the value read, only rightmost <code>howManyBits</code> are
	 *         valid, returns -1 if not enough bits left
	 */

	public int read(int howManyBits) {
		int retval = 0;
		if (myInput == null) {
			return -1;
		}

		while (howManyBits > myBitCount) {
			retval |= (myBuffer << (howManyBits - myBitCount));
			howManyBits -= myBitCount;
			try {
				if ((myBuffer = myInput.read()) == -1) {
					return -1;
				}
			} catch (IOException ioe) {
				System.err.println("error reading input file " + ioe);
			}
			myBitCount = BITS_PER_BYTE;
		}

		if (howManyBits > 0) {
			retval |= myBuffer >> (myBitCount - howManyBits);
			myBuffer &= bmask[myBitCount - howManyBits];
			myBitCount -= howManyBits;
		}
		return retval;
	}

	private void reset() {
		myBuffer = myBitCount = 0;
	}
}
